﻿using System;
using System.Collections.Generic;
using System.Linq;
using Crossroad.Core.Configs;
using SqlSugar;
using log4net.Core;
using Crossroad.Utils;
using Crossroad.Utils.Extensions;
using Crossroad.Core.Data;
using Crossroad.Core.Exceptions;
using Crossroad.Core.Helper;
using Crossroad.Core.Models;
using Crossroad.OAuth.Service;
using Crossroad.OAuth.Service.Impl;
using Crossroad.System.Admin.Service;
using Crossroad.System.Admin.Service.Impl;
using Crossroad.System.User.Const;
using Crossroad.System.User.Model;
using Crossroad.System.Admin.Model;
using Crossroad.System.Admin.Const;

namespace Crossroad.System.User.Service.Impl
{
    /// <summary>
    /// 用户信息
    ///  </summary>
    public class UserInfoService: Access<UserInfo>, IUserInfoService
    {
        private readonly ClientService clientService;
        private readonly AppInfoService appInfoService;
        private readonly RoleService roleService;

        public UserInfoService(
            IClientService clientServiceIpt,
            IAppInfoService appInfoServiceIpt,
            IRoleService roleServiceIpt
        )
        {
            clientService = clientServiceIpt as ClientService;
            appInfoService = appInfoServiceIpt as AppInfoService;
            roleService = roleServiceIpt as RoleService;
        }

        /// <summary>
        /// 分页查询
        /// </summary>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="method"></param>
        /// <param name="loadRelevantData">是否加载关联数据</param>
        public Pagination<UserInfo> GetPaging(
            int pageIndex,
            int pageSize,
            Action<ISugarQueryable<UserInfo>> method,
			bool loadRelevantData = true
        )
        {
            var query =  db.Queryable<UserInfo>();
            method(query);
            var totalCount = 0;
            var page = new Pagination<UserInfo>
            {
                PageIndex = pageIndex,
                PageSize = pageSize,
            };
            page.Records = query.ToPageList(page.PageIndex, page.PageSize, ref totalCount);
            page.TotalCount = totalCount;
			if(loadRelevantData)
                Load(page.Records);

            return page;
        }

        /// <summary>
        /// 查询列表
        /// </summary>
        /// <param name="method"></param>
        /// <param name="loadRelevantData">是否加载关联数据</param>
        /// <returns></returns>
        public  List<UserInfo> GetList(
            Action<ISugarQueryable<UserInfo>> method,
			bool loadRelevantData = true
        )
        {
            var query =  db.Queryable<UserInfo>();
            method(query);
            var list = query.ToList();
			if(loadRelevantData)
				Load(list);

            return list;
        }

        /// <summary>
        /// 字典数据返回
        /// </summary>
        /// <param name="method"></param>
        /// <returns></returns>
        public object GetDicList(
            Action<ISugarQueryable<UserInfo>> method
		)
        {
            var query =  db.Queryable<UserInfo>();
            method(query);

            return query.Select(m => new DicDom { label = m.nickName, value = m.id.ToString() }).ToArray();
        }

        /// <summary>
        /// 查询详情
        /// </summary>
        /// <param name="method"></param>
        /// <param name="loadRelevantData">是否加载关联数据</param>
        /// <returns></returns>
        public UserInfo GetEntity(
            Action<ISugarQueryable<UserInfo>> method,
			bool loadRelevantData = true
		)
        {
            var query =  db.Queryable<UserInfo>();
            method(query);
            var model = query.First();
            if (loadRelevantData)
                Load(model);

            return model;
        }

        /// <summary>
        /// 查询详情
        /// </summary>
        /// <param name="id"></param>
        /// <param name="loadRelevantData">是否加载关联数据</param>
        /// <returns></returns>
        public UserInfo GetEntity(int id, bool loadRelevantData = true)
        {
            var model = db.Queryable<UserInfo>().InSingle(id);
            if (loadRelevantData)
                Load(model);

            return model;
        }

        /// <summary>
        /// 根据id列表查询maps
        /// </summary>
        /// <param name="idList"></param>
        /// <returns></returns>
        public Dictionary<int, UserInfo> GetMaps(List<int> idList)
        {
            var maps = new Dictionary<int, UserInfo>();
            if (idList.Count < 1)
                return maps;

            var list = db.Queryable<UserInfo>()
                .In(f => f.id, idList.Distinct().ToArray())
                .ToList();

            list.ForEach(m =>
            {
                maps.Add(m.id, m);
            });

            return maps;
        }

        /// <summary>
        /// 查询存在的用户 根据用户名或电话
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public UserInfo Exists(UserInfo model)
        {
            var query = db.Queryable<UserInfo>();
            if(model.phone.Contains())
                query.Where(a => SqlFunc.Contains(a.username, model.username) || SqlFunc.Contains(a.phone, model.phone));
            else
                query.Where(a => SqlFunc.Contains(a.username, model.username));

            return query.First();
        }

        /// <summary>
        /// 新增返回详情
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public UserInfo CreateAt(UserInfo model)
        {
            var existUser = Exists(model);
            if (existUser.NotNull())
            {
                Load(existUser);
                return existUser;
            }

            model.uniqueKey = KeyUtil.UniqueKey;
            model.createTime = DateTime.Now.ToTimeStamp();
            if (model.avatar.Contains() && model.avatar.IndexOf("http") == 0)
            {
                var uri = new Uri(model.avatar);
                model.avatar = uri.AbsolutePath;
            }
            model = db.Insertable(model).ExecuteReturnEntity();

            return GetEntity(model.id);
        }

        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="model"></param>
		public UserInfo UpdateAt(UserInfo model)
        {
            if(model.id < 1)
                throw new LogicException("用户id不能为空！");

            model.uniqueKey = null;
            model.creatorId = null;
            model.createTime = null;
            if (model.avatar.Contains() && model.avatar.IndexOf("http") == 0)
            {
                var uri = new Uri(model.avatar);
                model.avatar = uri.AbsolutePath;
            }

            var updateable = db.Updateable(model).IgnoreColumns(true);
            var rows = updateable.ExecuteCommand();
            if (rows < 1)
                throw new LogicException("用户修改失败！");

			return GetEntity(model.id);
		}

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="source"></param>
        public void DeleteAt(UserInfo source)
        {
             var deleteable = db.Deleteable<UserInfo>();
             if (source.uniqueKey.Contains())
                 deleteable.Where(a => a.uniqueKey.Equals(source.uniqueKey));
            else
                deleteable.In(source.id);
            var rows = deleteable.ExecuteCommand();
            if (rows < 1)
                throw new LogicException("删除失败！");
        }


		#region 加载关联数据

        public void Load(List<UserInfo> list)
        {
            if (list.IsNull())
                return;

            var roleIdList = new List<int>();
            var organizationIdList = new List<int>();
            list.ForEach(model =>
            {
                if (model.avatar.Contains() && model.avatar.IndexOf("http") < 0)
                    model.avatar = $"{AppConfig.ServerHost}{model.avatar}";
                if (model.roleId.Contains())
                    roleIdList.Add(model.roleId.Value);
                if(model.organizationId.Contains())
                    organizationIdList.Add(model.organizationId.Value);
            });
            var roleMaps = roleService.GetMaps(roleIdList);
            var officeMaps = GetOfficeMaps(organizationIdList);
            list.ForEach(model =>
            {
                if (model.roleId.Contains() && roleMaps.ContainsKey(model.roleId.Value))
                    model.roleName = roleMaps[model.roleId.Value].name;
                if (model.organizationId.Contains() && officeMaps.ContainsKey(model.organizationId.Value))
                {
                    var office = officeMaps[model.organizationId.Value];
                    model.companyId = office.companyId;
                    model.companyName = office.companyName;
                    model.deptId = office.deptId;
                    model.deptName = office.deptName;
                }
            });
        }

        public void Load(UserInfo model)
        {
            if (model.IsNull())
                return;

            if (model.avatar.Contains() && model.avatar.IndexOf("http") < 0)
                model.avatar = $"{AppConfig.ServerHost}{model.avatar}";
            if (model.roleId.Contains())
                model.roleName = roleService.GetEntity(model.roleId, false).name;
            if (model.organizationId.Contains())
            {
                var office = GetOfficeInfo(model.organizationId.Value);
                if (office.NotNull())
                {
                    model.companyId = office.companyId;
                    model.companyName = office.companyName;
                    model.deptId = office.deptId;
                    model.deptName = office.deptName;
                }
            }
        }

        #endregion

        #region 业务逻辑

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="clientSecret"></param>
        /// <param name="appId"></param>
        /// <param name="loginMode"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public object Login(
            string clientId,
            string clientSecret,
            string appId,
            string loginMode,
            string username,
            string password
        )
        {
            if (clientId.IsNullOrEmpty())
                throw new LogicException("身份认证客户端id不能为空");
            if (clientSecret.IsNullOrEmpty())
                throw new LogicException("身份认证客户端密钥不能为空");
            if (appId.IsNullOrEmpty())
                throw new LogicException("应用id不能为空");
            if (username.IsNullOrEmpty())
                throw new LogicException("用户名不能为空");
            if (password.IsNullOrEmpty())
                throw new LogicException("密码不能为空");

            var client = clientService.GetList(queryable =>
                queryable.Where(a => a.clientId.Equals(clientId) && a.clientSecret.Equals(clientSecret)));
            if(client.IsNull())
                throw new LogicException("身份认证错误，非法登录");

            //应用信息查询
            var app = appInfoService.GetEntity(queryable => queryable.Where(a => a.name.Equals(appId)));
            if (app.NotNull())
                throw new LogicException("不存在的应用");

            switch (loginMode)
            {
                //标准登录 用户名密码
                case var t when t.Equals(LoginMode.std.ToString()):
                    return LoginStd(appId, t, username, password);
                case var t when t.Equals(LoginMode.sms.ToString()):
                    throw new LogException("短信登录功能 等待开发");
                case var t when t.Equals(LoginMode.wechat.ToString()):
                    throw new LogException("微信登录功能 等待开发");
                default:
                    throw new LogException("非法登录");
            }
        }

        /// <summary>
        /// 用户名密码登录
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="loginMode"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        private object LoginStd(string appId, string loginMode, string username, string password)
        {
            UserInfo user;
            user = GetEntity(queryable => queryable.Where(
                a => a.username.Equals(username) || a.phone.Equals(username)
            ));
            //用户信息查询
            if (user.IsNull())
                throw new LogicException("用户不存在！");
            if (!user.password.Equals(password))
                throw new LogicException("密码错误！");
            var userApp = db.Queryable<UserApp>()
                .Where(a => a.appId.Equals(appId) && a.loginMode.Equals(loginMode) && a.userId == user.id)
                .Take(1)
                .Single();
            if(userApp.IsNull())
                throw new LogicException("用户不存在");
            if(userApp.roleId.IsLessOrEqual0())
                throw new LogicException("该用户无角色权限无法登录！");

            //为用户创建访问令牌
            var token = clientService.CreateToken(appId, user.id);

            return new
            {
                currentUser = new
                {
                    user.uniqueKey,
                    user.nickName,
                    user.username,
                    user.password,
                },
                token
            };
        }

        /// <summary>
        /// 获取组织架构id对应的 公司单位及部门
        /// </summary>
        /// <param name="orgainizationId">组织架构id</param>
        /// <returns></returns>
        public OfficeInfo GetOfficeInfo(int orgainizationId)
        {
            var m = db.Queryable<Organization>().In(orgainizationId).Single();
            if (m.IsNull())
                return null;

            var props = new int[] { OrganizationProperty.Company.Val(), OrganizationProperty.Dept.Val() };
            var list = db.Queryable<Organization>()
                .Where(a => m.layerList.Contains(a.layerList))
                .In(a => a.propertyId, props)
                .Select(a => new { a.id, a.name, a.propertyId })
                .ToList();

            var company = list.FirstOrDefault(f => f.propertyId == OrganizationProperty.Company.Val());
            var dept = list.FirstOrDefault(f => f.propertyId == OrganizationProperty.Dept.Val());

            var office = new OfficeInfo
            {
                currentId = orgainizationId,
            };
            if (company.NotNull())
            {
                office.companyId = company.id;
                office.companyName = company.name;
            }
            if (dept.NotNull())
            {
                office.deptId = dept.id;
                office.deptName = dept.name;
            }

            return office;
        }

        /// <summary>
        /// 获取组织架构id对应的 公司单位及部门
        /// </summary>
        /// <param name="idList"></param>
        /// <returns></returns>
        public Dictionary<int, OfficeInfo> GetOfficeMaps(List<int> idList)
        {
            var maps = new Dictionary<int, OfficeInfo>();
            var list = db.Queryable<Organization>()
                .Select(a => new { a.parentId, a.id, a.name, a.propertyId, a.layerList })
                .ToList();
            var props = new int[] { OrganizationProperty.Company.Val(), OrganizationProperty.Dept.Val() };
            var arrId = idList.Distinct();
            foreach (var id in arrId)
            {
                var office = new OfficeInfo
                {
                    currentId = id,
                };
                var current = list.FirstOrDefault(f => f.id == id);
                if(current.IsNull())
                    continue;

                //工作单位 检索
                var ids = current.layerList.DelBothChar().Split(',').Select(str => str.ToInt()).ToArray();
                var items = from item in list where ids.Contains(item.id) && props.Contains(item.propertyId ?? 0) select item;
                var company = items.FirstOrDefault(f => f.propertyId == OrganizationProperty.Company.Val());
                var dept = items.FirstOrDefault(f => f.propertyId == OrganizationProperty.Dept.Val());
                if (company.NotNull())
                {
                    office.companyId = company.id;
                    office.companyName = company.name;
                }
                if (dept.NotNull())
                {
                    office.deptId = dept.id;
                    office.deptName = dept.name;
                }
                maps.Add(id, office);
            }

            return maps;
        }

        #endregion
    }
}
